{
  "nbformat": 4,
  "nbformat_minor": 0,
  "metadata": {
    "colab": {
      "provenance": [],
      "authorship_tag": "ABX9TyPzf1mcXxiTaX9ZTHRySdHi",
      "include_colab_link": true
    },
    "kernelspec": {
      "name": "python3",
      "display_name": "Python 3"
    },
    "language_info": {
      "name": "python"
    }
  },
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "view-in-github",
        "colab_type": "text"
      },
      "source": [
        "<a href=\"https://colab.research.google.com/github/sugarforever/LangChain-Tutorials/blob/main/LangChain_OpenAI_Function_Calling.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 72,
      "metadata": {
        "id": "ubbA-Eeluo1V"
      },
      "outputs": [],
      "source": [
        "!pip install langchain openai --upgrade --quiet"
      ]
    },
    {
      "cell_type": "markdown",
      "source": [
        "# OpenAI重大更新\n",
        "\n",
        "## 2023-06-13 函数调用和其他API更新\n",
        "\n",
        "[Function calling and other API updates](https://openai.com/blog/function-calling-and-other-api-updates)\n",
        "\n",
        "一些令人兴奋的更新：\n",
        "\n",
        "- 在Chat Completions API中新增了函数调用功能。\n",
        "- 更新了更可控的gpt-4和gpt-3.5-turbo版本。\n",
        "- 新增了gpt-3.5-turbo的16k上下文版本（相对于标准的4k版本）。\n",
        "- 我们的最先进的嵌入模型成本降低了75%。\n",
        "- gpt-3.5-turbo的输入令牌成本降低了25%。\n",
        "- 公布了gpt-3.5-turbo-0301和gpt-4-0314模型的停用时间表。\n",
        "\n",
        "**函数调用**功能使我们能够利用模型的自然语言理解能力，将人类语言有效地转化为结构化数据或在我们的代码中进行特定的函数调用。"
      ],
      "metadata": {
        "id": "oRx_yOHMU-FP"
      }
    },
    {
      "cell_type": "markdown",
      "source": [
        "### 基于OpenAI的Python SDK使用函数调用"
      ],
      "metadata": {
        "id": "MfTMkMDnWIc6"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "import os"
      ],
      "metadata": {
        "id": "6QU36FDAlSoU"
      },
      "execution_count": 73,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "os.environ['OPENAI_API_KEY'] = '您的有效OpenAI API Key'"
      ],
      "metadata": {
        "id": "eEpoh5lzu267"
      },
      "execution_count": 74,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "model = 'gpt-3.5-turbo-0613'"
      ],
      "metadata": {
        "id": "azjlHvHY7W0l"
      },
      "execution_count": 75,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "function_descriptions = [\n",
        "  {\n",
        "      \"name\": \"get_student_score\",\n",
        "      \"description\": \"Get the student score by given his or her name\",\n",
        "      \"parameters\": {\n",
        "          \"type\": \"object\",\n",
        "          \"properties\": {\n",
        "              \"name\": {\n",
        "                  \"type\": \"string\",\n",
        "                  \"description\": \"The student's name\",\n",
        "              }\n",
        "          },\n",
        "          \"required\": [\"name\"],\n",
        "      },\n",
        "    }\n",
        "]"
      ],
      "metadata": {
        "id": "yXS01m6D6hoM"
      },
      "execution_count": 76,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "user_query = \"What's the performance of Lucy in the school this year?\""
      ],
      "metadata": {
        "id": "3bWwHFxi6oiY"
      },
      "execution_count": 81,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "import openai"
      ],
      "metadata": {
        "id": "6e4tFuPfUHQ8"
      },
      "execution_count": 82,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "response = openai.ChatCompletion.create(\n",
        "    model=model,\n",
        "    messages=[{\"role\": \"user\", \"content\": user_query}],\n",
        "    functions=function_descriptions,\n",
        "    function_call=\"auto\",\n",
        ")"
      ],
      "metadata": {
        "id": "ZwhqCaeN6qxx"
      },
      "execution_count": 83,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "ai_response_message = response[\"choices\"][0][\"message\"]\n",
        "print(ai_response_message)"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "1TmY2L8S6sfu",
        "outputId": "ea415832-41bc-48cf-f057-5faad284a059"
      },
      "execution_count": 84,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "{\n",
            "  \"role\": \"assistant\",\n",
            "  \"content\": null,\n",
            "  \"function_call\": {\n",
            "    \"name\": \"get_student_score\",\n",
            "    \"arguments\": \"{\\n\\\"name\\\": \\\"Lucy\\\"\\n}\"\n",
            "  }\n",
            "}\n"
          ]
        }
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "name = eval(ai_response_message['function_call']['arguments']).get(\"name\")"
      ],
      "metadata": {
        "id": "LJeW8xGa6vlZ"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "name"
      ],
      "metadata": {
        "id": "g94jpgyp-bwm"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "import json\n",
        "\n",
        "SCORES = { 'Alex': 90, 'Lucy': 60 }\n",
        "def get_student_score(name):\n",
        "\n",
        "    \"\"\"Get the student score by given his or her name\"\"\"\n",
        "\n",
        "    score = {\n",
        "        \"name\": name,\n",
        "        \"score\": SCORES[name]\n",
        "    }\n",
        "    return json.dumps(score)"
      ],
      "metadata": {
        "id": "0Hh2KajS92tX"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "function_response = get_student_score(name=name)\n",
        "\n",
        "function_response"
      ],
      "metadata": {
        "id": "TbNNhRzx7KLt"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "second_response = openai.ChatCompletion.create(\n",
        "    model=model,\n",
        "    messages=[\n",
        "        {\"role\": \"user\", \"content\": user_query},\n",
        "        ai_response_message,\n",
        "        {\n",
        "            \"role\": \"function\",\n",
        "            \"name\": \"get_student_score\",\n",
        "            \"content\": function_response,\n",
        "        },\n",
        "    ],\n",
        ")"
      ],
      "metadata": {
        "id": "Lb86hriN7RXR"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "print (second_response['choices'][0]['message']['content'])"
      ],
      "metadata": {
        "id": "TCtc3z2o7U3O"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "### 基于LangChain框架使用函数调用"
      ],
      "metadata": {
        "id": "jlhXtIMxWZtD"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "from langchain.chat_models import ChatOpenAI\n",
        "from langchain.schema import HumanMessage, AIMessage, ChatMessage\n",
        "from langchain.tools import format_tool_to_openai_function, YouTubeSearchTool"
      ],
      "metadata": {
        "id": "KaAuKKAYvH-J"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "llm = ChatOpenAI(model=model)"
      ],
      "metadata": {
        "id": "xZF8GCXevKmF"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "#### LangChain工具(Tool)的OpenAI函数调用能力"
      ],
      "metadata": {
        "id": "0T4v7FXEWoAI"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "tools = [YouTubeSearchTool()]\n",
        "functions = [format_tool_to_openai_function(t) for t in tools]"
      ],
      "metadata": {
        "id": "-HUoZgHavMd4"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "functions"
      ],
      "metadata": {
        "id": "IJAVCMCesbsD"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "message = llm.predict_messages([HumanMessage(content='search videos in the topic of OpenAI on Youtube')], functions=functions)"
      ],
      "metadata": {
        "id": "RXkzJV-lvPaJ"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "message.additional_kwargs"
      ],
      "metadata": {
        "id": "eim7cTNvvRkL"
      },
      "execution_count": null,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "#### LangChain使用OpenAI函数调用实例"
      ],
      "metadata": {
        "id": "UCAZLiZoWyH8"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "function_descriptions = [\n",
        "    {\n",
        "        \"name\": \"remove_word_from_string\",\n",
        "        \"description\": \"Remove a word from a string by given its index\",\n",
        "        \"parameters\": {\n",
        "            \"type\": \"object\",\n",
        "            \"properties\": {\n",
        "                \"string\": {\n",
        "                    \"type\": \"string\",\n",
        "                    \"description\": \"The original string to be processed\",\n",
        "                },\n",
        "                \"index\": {\n",
        "                    \"type\": \"integer\",\n",
        "                    \"description\": \"The index of the word to be removed\"\n",
        "                },\n",
        "            },\n",
        "            \"required\": [\n",
        "                \"string\",\n",
        "                \"index\"\n",
        "            ],\n",
        "        },\n",
        "    },\n",
        "    {\n",
        "        \"name\": \"send_message_by_email\",\n",
        "        \"description\": \"Send an email with the text message to a recipient\",\n",
        "        \"parameters\": {\n",
        "            \"type\": \"object\",\n",
        "            \"properties\": {\n",
        "                \"recipient\": {\n",
        "                    \"type\": \"string\",\n",
        "                    \"description\": \"The email address of the recipient\",\n",
        "                },\n",
        "                \"message\": {\n",
        "                    \"type\": \"string\",\n",
        "                    \"description\": \"The message of the email content\",\n",
        "                }\n",
        "            },\n",
        "            \"required\": [\n",
        "                \"recipient\",\n",
        "                \"message\"\n",
        "            ],\n",
        "        },\n",
        "    }\n",
        "]\n"
      ],
      "metadata": {
        "id": "EUR3ow27vVdr"
      },
      "execution_count": 85,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "question = \"\"\"\n",
        "I have a string as follows:\n",
        "\n",
        "black yellow red blue green\n",
        "\n",
        "Please do the following 2 operations on it:\n",
        "1. Remove the third word in the string\n",
        "2. Send the updated string to Alex via email alex@xyz.com\n",
        "\"\"\""
      ],
      "metadata": {
        "id": "wX0Vi0GVvXfF"
      },
      "execution_count": 86,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "source": [
        "Helper functions to get function parameter names. They will be used to implement dynamic function calls in Python."
      ],
      "metadata": {
        "id": "FWQFJ0OaLT4N"
      }
    },
    {
      "cell_type": "code",
      "source": [
        "import inspect\n",
        "\n",
        "def get_function_parameter_names(function):\n",
        "  if function is not None and inspect.isfunction(function):\n",
        "      parameter_names = inspect.signature(function).parameters.keys()\n",
        "      return list(parameter_names)\n",
        "  else:\n",
        "      return None"
      ],
      "metadata": {
        "id": "7Z9GjqAgLYFq"
      },
      "execution_count": 87,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "def remove_word_from_string(string, index):\n",
        "    words = string.split()\n",
        "\n",
        "    if 0 <= index < len(words):\n",
        "        del words[index]\n",
        "\n",
        "        return ' '.join(words)\n",
        "    else:\n",
        "        return string\n",
        "\n",
        "def send_message_by_email(recipient, message):\n",
        "    print(f'Sending {message} to {recipient}')\n",
        "    return f'Just sent email to {recipient}'"
      ],
      "metadata": {
        "id": "ZW_YcePsLZV8"
      },
      "execution_count": 88,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "parameter_names = get_function_parameter_names(remove_word_from_string)\n",
        "parameter_names"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "U5Rp8oAxLkgA",
        "outputId": "a40a758a-ccbb-4c0d-f7c5-d89792cea2c2"
      },
      "execution_count": 89,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "['string', 'index']"
            ]
          },
          "metadata": {},
          "execution_count": 89
        }
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "first_response = llm.predict_messages([HumanMessage(content=question)], functions=function_descriptions)\n",
        "first_response"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "IKDhcFLjvZzR",
        "outputId": "51b52683-ad91-41ff-91f0-cb6081581ed5"
      },
      "execution_count": 90,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "AIMessage(content='', additional_kwargs={'function_call': {'name': 'remove_word_from_string', 'arguments': '{\\n  \"string\": \"black yellow red blue green\",\\n  \"index\": 2\\n}'}}, example=False)"
            ]
          },
          "metadata": {},
          "execution_count": 90
        }
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "first_response.additional_kwargs"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "avfWX8RlvbVN",
        "outputId": "db308331-5800-4622-b1aa-97ed06b2634f"
      },
      "execution_count": 91,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "{'function_call': {'name': 'remove_word_from_string',\n",
              "  'arguments': '{\\n  \"string\": \"black yellow red blue green\",\\n  \"index\": 2\\n}'}}"
            ]
          },
          "metadata": {},
          "execution_count": 91
        }
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "# Get function name, and its arguments\n",
        "\n",
        "function_name = first_response.additional_kwargs[\"function_call\"][\"name\"]\n",
        "arguments = json.loads(first_response.additional_kwargs[\"function_call\"][\"arguments\"])\n",
        "\n",
        "# Locate the function and make the call\n",
        "the_function = globals().get(function_name)\n",
        "parameter_names = get_function_parameter_names(the_function)\n",
        "parameter_values = []\n",
        "for parameter_name in parameter_names:\n",
        "  parameter_values.append(arguments[parameter_name])\n",
        "\n",
        "returned_value = the_function(*parameter_values)"
      ],
      "metadata": {
        "id": "KbT440rwvdIR"
      },
      "execution_count": 92,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "print(returned_value)"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "dyDi8HFNR2AP",
        "outputId": "f226a3f1-63f0-4789-c7a6-63657ed0b60c"
      },
      "execution_count": 93,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "black yellow blue green\n"
          ]
        }
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "second_response = llm.predict_messages(\n",
        "    [\n",
        "        HumanMessage(content=question),\n",
        "        AIMessage(content=str(first_response.additional_kwargs)),\n",
        "        ChatMessage(\n",
        "            role='function',\n",
        "            additional_kwargs = {'name': function_name},\n",
        "            content = returned_value\n",
        "        )\n",
        "    ],\n",
        "    functions=function_descriptions\n",
        ")"
      ],
      "metadata": {
        "id": "He9SO-aRviNi"
      },
      "execution_count": 94,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "second_response.additional_kwargs"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "qDIOnSVJvkCw",
        "outputId": "bcff9ca6-18cd-47e7-cbde-cb3020ee1049"
      },
      "execution_count": 95,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "{'function_call': {'name': 'send_message_by_email',\n",
              "  'arguments': '{\\n  \"recipient\": \"alex@xyz.com\",\\n  \"message\": \"black yellow blue green\"\\n}'}}"
            ]
          },
          "metadata": {},
          "execution_count": 95
        }
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "second_response.content"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 36
        },
        "id": "IGmrBfsmCxv5",
        "outputId": "79ba3bd0-d36c-4a66-ebd3-463eed5c7b9f"
      },
      "execution_count": 96,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "'The updated string after removing the third word is: \"black yellow blue green\".\\n\\nNow, I will send the updated string to Alex via email.'"
            ],
            "application/vnd.google.colaboratory.intrinsic+json": {
              "type": "string"
            }
          },
          "metadata": {},
          "execution_count": 96
        }
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "# Again get function name, and its arguments\n",
        "\n",
        "function_name = second_response.additional_kwargs[\"function_call\"][\"name\"]\n",
        "arguments = json.loads(second_response.additional_kwargs[\"function_call\"][\"arguments\"])\n",
        "\n",
        "# Locate the function and make the call\n",
        "the_function = globals().get(function_name)\n",
        "parameter_names = get_function_parameter_names(the_function)\n",
        "parameter_values = []\n",
        "for parameter_name in parameter_names:\n",
        "  parameter_values.append(arguments[parameter_name])\n",
        "\n",
        "returned_value = the_function(*parameter_values)"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "fnxm88ivvmCE",
        "outputId": "10793fa3-6509-4ac8-fc2f-982a78849ad7"
      },
      "execution_count": 97,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Sending black yellow blue green to alex@xyz.com\n"
          ]
        }
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "# Print the returned value in the second function call\n",
        "print(returned_value)"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "nmQHgIMESjDB",
        "outputId": "be6377a1-454a-472e-8966-17e4204874d6"
      },
      "execution_count": 98,
      "outputs": [
        {
          "output_type": "stream",
          "name": "stdout",
          "text": [
            "Just sent email to alex@xyz.com\n"
          ]
        }
      ]
    },
    {
      "cell_type": "code",
      "source": [
        "third_response = llm.predict_messages(\n",
        "    [\n",
        "        HumanMessage(content=question),\n",
        "        AIMessage(content=str(first_response.additional_kwargs)),\n",
        "        AIMessage(content=str(second_response.additional_kwargs)),\n",
        "        ChatMessage(\n",
        "            role='function',\n",
        "            additional_kwargs = {'name': function_name},\n",
        "            content = returned_value\n",
        "        )\n",
        "    ], functions=function_descriptions\n",
        ")"
      ],
      "metadata": {
        "id": "fwQ06ZiIvofH"
      },
      "execution_count": 99,
      "outputs": []
    },
    {
      "cell_type": "code",
      "source": [
        "third_response"
      ],
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "6bQoHrz2vqP3",
        "outputId": "890ad015-2c65-43ac-e56b-64e7c237013f"
      },
      "execution_count": 100,
      "outputs": [
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "AIMessage(content='I have removed the third word from the string and sent the updated string to Alex via email.', additional_kwargs={}, example=False)"
            ]
          },
          "metadata": {},
          "execution_count": 100
        }
      ]
    },
    {
      "cell_type": "code",
      "source": [],
      "metadata": {
        "id": "sQ2TeDb7bVQ1"
      },
      "execution_count": null,
      "outputs": []
    }
  ]
}